package main

import (
	"context"
	"fmt"
	"log"
	"math/rand"
	"net/http"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
)

var VERSION string
var ADDR string
var functionLatency = createExecutionTimeMetric("httpserver", "Time spent.")

// 执行时间
type ExecutionTimer struct {
	histo *prometheus.HistogramVec
	start time.Time
	last  time.Time
}

// 程序初始化读取环境变量
func init() {
	VERSION = os.Getenv("VERSION")
	host := getEnvOrDefault("SERVER_ADDR", "0.0.0.0")
	port := getEnvOrDefault("SERVER_PORT", "80")
	log.Printf("env {VERSION} : %s, {SERVER_ADDR} : %s, {SERVER_PORT} : %s", VERSION, host, port)
	ADDR = fmt.Sprintf("%s:%s", host, port)
}

// 从环境变量读取数据
func getEnvOrDefault(key string, defaultValue string) string {
	value := os.Getenv(key)
	if value == "" {
		return defaultValue
	}
	return value
}

// 请求处理函数
func rootHandler(w http.ResponseWriter, r *http.Request) {
	timer := newExecutionTimer(functionLatency)
	defer timer.ObserveTotal()
	delay := randInt(100, 3000)

	// 随机休眠时间
	time.Sleep(time.Microsecond * time.Duration(delay))

	w.Header().Add("VERSION", VERSION)
	w.WriteHeader(http.StatusOK)
	for name, header := range r.Header {
		for _, value := range header {
			w.Header().Add(name, value)
		}
	}
	//fmt.Fprintf(w, "this is http server")

	// 转发请求
	req, err := http.NewRequest("GET", "http://service2", nil)
	if err != nil {
		fmt.Errorf("fail to new request service2", err)
	}
	lowerCaseHeader := make(http.Header)
	for key, value := range r.Header {
		lowerCaseHeader[strings.ToLower(key)] = value
	}
	req.Header = lowerCaseHeader
	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		fmt.Errorf("fail to send to service2", err)
	} else {
		log.Println("Http get service2 success")
	}
	if resp != nil {
		resp.Write(w)
	}

	// 记录访问日志
	accessLog := fmt.Sprintf("access log => remote ip : %s, method : %s, path : %s, query : %s, delay : %dms",
		r.RemoteAddr, r.Method, r.RequestURI, r.URL.RawQuery, delay)
	log.Println(accessLog)

}

// healthz 健康检查函数
func healthz(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusOK)
	log.Printf("healthz remote ip : %s\n", r.RemoteAddr)
}

// 在int范围的随机函数
func randInt(min int, max int) int {
	rand.Seed(time.Now().UTC().UnixNano())
	return min + rand.Intn(max-min)
}

// 注册metric
func registerMetric() {
	err := prometheus.Register(functionLatency)
	if err != nil {
		log.Fatalf("prometheus register fail：%s\n", err)
	}
	log.Println("prometheus register success")
}

func newExecutionTimer(histo *prometheus.HistogramVec) *ExecutionTimer {
	now := time.Now()
	return &ExecutionTimer{
		histo: histo,
		start: now,
		last:  now,
	}
}

func (t *ExecutionTimer) ObserveTotal() {
	(*t.histo).WithLabelValues("total").Observe(time.Since(t.start).Seconds())
}

// 创建执行时间耗时metric
func createExecutionTimeMetric(namespace string, help string) *prometheus.HistogramVec {
	return prometheus.NewHistogramVec(
		prometheus.HistogramOpts{
			Namespace: namespace,
			Name:      "execution_latency_seconds",
			Help:      help,
			Buckets:   prometheus.ExponentialBuckets(0.001, 2, 15),
		}, []string{"step"},
	)
}

func main() {
	// 注册metric
	registerMetric()

	// 初始化handler
	mux := http.NewServeMux()
	mux.HandleFunc("/", rootHandler)
	mux.HandleFunc("/healthz", healthz)
	mux.Handle("/metrics", promhttp.Handler())

	// 初始化server
	svc := http.Server{
		Addr:    ADDR,
		Handler: mux,
	}
	// 监听系统信号
	signalChan := make(chan os.Signal, 3)
	signal.Notify(signalChan, syscall.SIGTERM, syscall.SIGINT, syscall.SIGQUIT)
	// 协程中启动server
	go func() {
		err := svc.ListenAndServe()
		if err != nil && err != http.ErrServerClosed {
			log.Fatalf("http server start fail：%s\n", err)
		}
	}()
	log.Printf("http server started at %s\n", ADDR)

	// 等待系统信号
	<-signalChan

	// 停止server
	baseCtx := context.Background()
	timeoutCtx, cancel := context.WithTimeout(baseCtx, 1*time.Second)
	defer cancel()
	err := svc.Shutdown(timeoutCtx)
	if err != nil {
		log.Fatalf("http server shutdown fail %s\n", err)
	}
	log.Println("http server shutdown success")
}
